package xfuzzy.xfghl.view;

import xfuzzy.util.*;

import javax.swing.*;
import java.awt.*;

/**
 * Clase que realiza la representacion de la evolucion del algoritmo genetico
 * 
 * @author Alberto David Fernandez Fernandez
 * 
 */
public class GeneticDataGraphPanel extends JPanel {

	/**
	 * Codigo asociado a la clase serializable
	 */
	private static final long serialVersionUID = 95505666603068L;

	/**
	 * Altura del panel
	 */
	private final static int HEIGHT = 300;

	/**
	 * Factor de cambio de base de los algoritmos
	 */
	private final static double log10 = Math.log(10.0);

	// ----------------------------------------------------------------------------//
	// MIEMBROS PRIVADOS //
	// ----------------------------------------------------------------------------//

	private Graphics2D gc, gd;
	private Graphics2D gtrn_error;
	private Graphics2D gtst_error;
	private Dimension size;
	private int x0, y0, x1, y1;
	private int yresc = 3;
	private int xresc = 0;
	private int xscale = 20;
	private int pos = 0;
	private GeneticDataGraphPoint[] hist;
	//private Xfsl xfsl;
	//private boolean classif;

	// ----------------------------------------------------------------------------//
	// CONSTRUCTOR //
	// ----------------------------------------------------------------------------//

	/**
	 * Constructor
	 */
	public GeneticDataGraphPanel() {
		super();
		Dimension prefsize = new Dimension(getPreferredSize().width, HEIGHT);
		setPreferredSize(prefsize);
		setBackground(XConstants.textbackground);
		setBorder(BorderFactory.createLoweredBevelBorder());
	}

	// ----------------------------------------------------------------------------//
	// METODOS PUBLICOS //
	// ----------------------------------------------------------------------------//

	/**
	 * Limpiar la representacion
	 */
	public void reset() {
		yresc = 3;
		xresc = 0;
		xscale = 20;
		pos = 0;
		repaint();
	}
	
	public void setSizeHist(int size)  {
		
		hist = new GeneticDataGraphPoint[size + 1];
		pos = 0;
	}
	
	public void update(int size)  {
		
		xscale = size / 5;
		this.setSizeHist(size);
		paintAxis();
		repaint();
	}

	/**
	 * Añadir un dato a la representacion
	 */
	public void addStatus(double status) {

		GeneticDataGraphPoint p = new GeneticDataGraphPoint();
		p.setYre(ypos(status));
		p.setX(x0 + (((x1 - x0) * pos) / hist.length));
		paintStatus(p);
		hist[pos] = p;
		pos++;
	}

	/**
	 * Pintar el panel
	 */
	public void paint(Graphics g) {
		super.paint(g);
		setVars(g);
		paintLeyend();
		paintAxis();
		paintHist();
	}

	// ----------------------------------------------------------------------------//
	// METODOS PRIVADOS //
	// ----------------------------------------------------------------------------//

	/**
	 * Calcula las variables internas
	 */
	private void setVars(Graphics g) {
		this.gc = (Graphics2D) g;
		this.gtrn_error = (Graphics2D) g.create();
		gtrn_error.setColor(Color.blue);

		float dash[] = { 4.0f, 4.0f };
		this.gd = (Graphics2D) g.create();
		this.gd.setStroke(new BasicStroke(1.0f, 2, 0, 10.0f, dash, 0.0f));
		this.gtst_error = (Graphics2D) gd.create();
		gtst_error.setColor(Color.blue);

		this.size = getSize();
		this.x0 = 170;
		this.y0 = size.height - 50;
		this.x1 = size.width - 110;
		this.y1 = 30;
	}

	/**
	 * Dibuja la leyenda de la representacion grafica
	 */
	private void paintLeyend() {
			gc.drawString("Error", 20, 60);
			gtrn_error.drawLine(60, 55, 100, 55);
	}

	/**
	 * Dibuja los ejes de la representacion grafica
	 */
	private void paintAxis() {
		gc.drawString("Evaluations", size.width - 110, size.height - 20);
		gc.drawLine(x0, y1, x0, y0 + 1);
		gc.drawLine(x0, y0 + 1, x1, y0 + 1);
		gc.drawLine(x1, y1, x1, y0 + 1);
		FontMetrics fm = getFontMetrics(getFont());

		for (int i = 0; i < yresc; i++) {
			int ypos = y1 + (y0 - y1) * i / yresc;
			String label = "1.0" + (i == 0 ? "" : "E-" + i);
			int xpos = fm.stringWidth(label) + 10;
			gc.drawLine(x0 - 4, ypos, x0, ypos);
			gc.drawString(label, x0 - xpos, ypos + 5);
			gd.drawLine(x0, ypos, x1, ypos);
		}
		for (int i = 0; i <= 5; i++) {
			int iter = i * xscale;
			int xpos = x0 + (x1 - x0) * i / 5;
			int spos = fm.stringWidth("" + iter) / 2;
			gc.drawLine(xpos, y0 + 5, xpos, y0 + 1);
			gc.drawString("" + iter, xpos - spos, y0 + 21);
		}
	}

	/**
	 * Dibuja la evolucion historica
	 */
	private void paintHist() {
		GeneticDataGraphPoint p0, p1;
		if (pos == 0)
			return;
		p0 = hist[0];
		for (int i = 1; i < pos; i++) {
			p1 = hist[i];
			paintPoints(p0, p1);
			p0 = p1;
		}
	}

	/**
	 * Dibuja un nuevo punto en la evolucion
	 */
	private void paintStatus(GeneticDataGraphPoint status) {
		if (pos == 0)
			return;
		setVars(getGraphics());
		GeneticDataGraphPoint p0 = hist[pos - 1];
		GeneticDataGraphPoint p1 = status;
		if (verticalOutOfBounds(p0) || verticalOutOfBounds(p1)) {
			yresc++;
			repaint();
		} else if (horizontalOutOfBounds(p1)) {
			rescale();
			repaint();
		} else
			paintPoints(p0, p1);
	}

	/**
	 * Dibuja un segmento en la evolucion
	 */
	private void paintPoints(GeneticDataGraphPoint p0, GeneticDataGraphPoint p1) {
		gtrn_error.drawLine(p0.getX(), p0.getYre(), p1.getX(), p1.getYre());
	}

	/**
	 * Aumenta en un grado la escala temporal
	 */
	private void rescale() {
		for (int i = 0; 2 * i < pos && 2 * i < hist.length; i++)
			hist[i] = hist[2 * i];
		pos = pos / 2;
		xresc++;
		xscale = 20;
		for (int i = 0; i < xresc; i++)
			if (i % 3 != 0)
				xscale *= 2;
			else
				xscale = xscale * 5 / 2;
	}

	/**
	 * Calcula la posicion de un valor sobre la coordenada Y
	 */
	private int ypos(double value) {
		if (value <= 1e-10)
			return y0;
		return (int) (y1 - (y0 - y1) * Math.log(value) / (yresc * log10));
	}

	/**
	 * Detecta si un punto se sale de los limites de la grafica
	 */
	private boolean verticalOutOfBounds(GeneticDataGraphPoint p) {
		if (p.getYre() > y0)
			return true;
		return false;
	}

	/**
	 * Detecta si un punto se sale de los limites de la grafica
	 */
	private boolean horizontalOutOfBounds(GeneticDataGraphPoint p) {
		return (p.getX() > x1);
	}
}